﻿using System.Text;
using System.Text.RegularExpressions;

namespace System
{
    public static class StringExtension
    {
        /// <summary>确保字符串不超过指定的长度</summary>
        public static string EnsureLength(this string @this, int length)
        {
            if (string.IsNullOrEmpty(@this)) return @this;
            if (@this.Length <= length) return @this;

            return @this[0..length];
        }

        /// <summary>确保字符串以指定的另一字符串结束，不区分大小写</summary>
        /// <param name="@this">字符串</param>
        /// <param name="end">结束字符串</param>
        /// <returns></returns>
        public static string EnsureEnd(this string @this, string end)
        {
            if (string.IsNullOrEmpty(end)) return @this;
            if (string.IsNullOrEmpty(@this)) return end;

            if (@this.EndsWith(end, StringComparison.OrdinalIgnoreCase)) return @this;

            return @this + end;
        }

        /// <summary>从当前字符串开头移除另一字符串，不区分大小写，循环多次匹配前缀</summary>
        /// <param name="str">当前字符串</param>
        /// <param name="starts">另一字符串</param>
        /// <returns></returns>
        public static string TrimStart(this string @this, params string[] starts)
        {
            if (string.IsNullOrEmpty(@this) ||
                starts == null || starts.Length < 1)
            {
                return @this;
            }

            for (var i = 0; i < starts.Length; i++)
            {
                if (string.IsNullOrEmpty(starts[i])) continue;
                if (@this.StartsWith(starts[i], StringComparison.OrdinalIgnoreCase))
                {
                    @this = @this.Substring(starts[i].Length);
                    if (string.IsNullOrEmpty(@this)) break;

                    // 从头开始
                    i = -1;
                }
            }
            return @this;
        }

        /// <summary>从当前字符串结尾移除另一字符串，不区分大小写，循环多次匹配后缀</summary>
        /// <param name="str">当前字符串</param>
        /// <param name="ends">另一字符串</param>
        /// <returns></returns>
        public static string TrimEnd(this string str, params string[] ends)
        {
            if (string.IsNullOrEmpty(str)) return str;
            if (ends == null || ends.Length < 1 || string.IsNullOrEmpty(ends[0])) return str;

            for (var i = 0; i < ends.Length; i++)
            {
                if (str.EndsWith(ends[i], StringComparison.OrdinalIgnoreCase))
                {
                    str = str.Substring(0, str.Length - ends[i].Length);
                    if (string.IsNullOrEmpty(str)) break;

                    // 从头开始
                    i = -1;
                }
            }
            return str;
        }

        /// <summary>移除字符串的指定字符</summary>
        /// <param name="@this"></param>
        /// <param name="chars">需要移除的字符</param>
        /// <returns></returns>
        public static string RemoveCharacters(this string @this, IEnumerable<char> chars)
        {
            return string.IsNullOrEmpty(@this) ? string.Empty : new string(@this.Where(c => !chars.Contains(c)).ToArray());
        }

        /// <summary>移除字符串中的特殊字符</summary>
        /// <param name="@this"></param> 
        /// <returns></returns>
        public static string RemoveSpecial(this string @this)
        {
            var special = new char[] { '	' };
            return @this.RemoveCharacters(special);
        }

        /// <summary>将字符串的首字母转成小写</summary>
        public static string FirstCharToLowerCase(this string @this)
        {
            if (@this.IsNotNullAndWhiteSpace() && @this.Length > 2 && char.IsUpper(@this[0]))
            {
                return char.ToLower(@this[0]) + @this.Substring(1);
            }
            if (@this.Length == 2)
            {
                return @this.ToLower();
            }
            return @this;
        }

        /// <summary>移除字符串的html标签</summary>
        /// <param name="@this"></param>
        /// <returns></returns>
        public static string RemoveHtmlTag(this string @this)
        {
            if (string.IsNullOrWhiteSpace(@this)) return @this;
            var strText = Regex.Replace(@this, "<[^>]+>", " ");
            strText = Regex.Replace(strText, "&[^;]+;", " ");
            return strText;
        }

        #region URL编码和URL解码

        /// <summary>UrlEncode编码</summary>
        /// <param name="@this">要编码的文本</param>
        /// <returns></returns>
        public static string UrlEncode(this string @this) => Uri.EscapeDataString(@this);

        /// <summary>UrlEncode解码</summary>
        /// <param name="@this">要编码的文本</param>
        /// <returns></returns>
        public static string UrlDecode(this string @this) => Uri.UnescapeDataString(@this);

        #endregion

        /// <summary>将指定字符串中的格式项替换为指定数组中相应对象的字符串表示形式。</summary>
        /// <param name="this"></param>
        /// <param name="args"></param>
        /// <returns></returns>
        public static string FormatStr(this string @this, params object[] args)
        {
            if (String.IsNullOrWhiteSpace(@this)) return @this;

            // 特殊处理时间格式化。这些年，无数项目实施因为时间格式问题让人发狂
            for (var i = 0; i < args.Length; i++)
            {
                if (args[i] is DateTime dt)
                {
                    // 没有写格式化字符串的时间参数，一律转为标准时间字符串
                    if (@this.Contains("{" + i + "}")) args[i] = dt.ToString("yyyy-MM-dd HH:mm:ss");
                }
            }
            return String.Format(@this, args);
        }

        public static string ToCacheKey(this string @this, params object[] cacheParams)
        {
            if (cacheParams != null && cacheParams.Count() > 0)
            {
                return $"{@this.ToLower()}:{string.Join("_", cacheParams)}";
            }
            else
            {
                return @this.ToLower();
            }
        }

        public static bool RegexIsMatch(this string str, string pattern)
        {
            Regex regex = new Regex(pattern);
            return regex.IsMatch(str);
        }

        /// <summary>
        /// 分割符转换
        /// </summary>
        /// <param name="inputString"></param>
        /// <returns></returns>
        public static string ConvertSplitChar(this string inputString, string splitString)
        {
            Regex replaceRegex = new Regex(@"[\s，,、；;]+", RegexOptions.IgnorePatternWhitespace);
            inputString = replaceRegex.Replace(inputString, splitString);
            return inputString;
        }

        /// <summary>将字符串反转</summary>
        public static string Reverse(this string @this)
        {

            var charStr = @this.ToCharArray();

            Array.Reverse(charStr);

            Text.StringBuilder sb = new Text.StringBuilder();

            foreach (var item in charStr)
            {
                sb.Append(item);
            }

            return sb.ToString();
        }

        public static string ToAnonymous(this string @this, bool isPhone = false)
        {
            if (string.IsNullOrWhiteSpace(@this) || @this.Length < 3)
            {
                return @this;
            }
            if (@this.Length == 3)
            {
                return $"{@this.First()}*{@this.Last()}";
            }
            try
            {
                var length = @this.Length;
                var prefixLength = (int)Math.Ceiling((double)length / 3);
                var suffixLength = (int)Math.Floor((double)length / 3);
                if (isPhone)
                {
                    prefixLength = (int)Math.Floor((double)length / 3);
                    suffixLength = (int)Math.Ceiling((double)length / 3);
                }
                var anonymousLength = length - prefixLength - suffixLength;
                return $"{@this[..prefixLength]}{"*".PadLeft(anonymousLength, '*')}{@this[^suffixLength..]}";
            }
            catch { return @this; }
        }

        public static string ToLog(this string @this, string logValue, bool isNewLine = false)
        {
            if (string.IsNullOrWhiteSpace(logValue)) return string.Empty;

            return $"{(isNewLine ? "<br/>" : "")}{@this}：{logValue}";
        }

        public static string ToLogAppend(this string @this, string logValue)
        => string.IsNullOrWhiteSpace(logValue) ? @this : $"{@this}：{logValue}";

        public static string ToLogAppendAndNull(this string @this, string logValue, bool isNewLine = false)
        => $"{(isNewLine ? "<br/>" : "")}{@this}：{(string.IsNullOrWhiteSpace(logValue) ? "(空)" : logValue)}";

        public static string ToLog(this string @this, string oldValue, string newValue, bool isNewLine = false)
        {
            if (string.Equals(oldValue, newValue)) return string.Empty;

            var strOld = string.IsNullOrWhiteSpace(oldValue) ? "(空)" : $"【{oldValue}】";
            var strNew = string.IsNullOrWhiteSpace(newValue) ? "(空)" : $"【{newValue}】";
            return $"{(isNewLine ? "<br/>" : "")}{@this}{strOld}改为{strNew}";
        }

        public static string ToLog(this string @this, long oldValue, long newValue, bool isNewLine = false)
        => oldValue == newValue ? string.Empty : $"{(isNewLine ? "<br/>" : "")}{@this}【{oldValue}】改为【{newValue}】";

        public static string ToLog(this string @this, int oldValue, int newValue, bool isNewLine = false)
        => oldValue == newValue ? string.Empty : $"{(isNewLine ? "<br/>" : "")}{@this}【{oldValue}】改为【{newValue}】";

        public static string ToLog(this string @this, decimal oldValue, decimal newValue, bool isNewLine = false)
        => oldValue == newValue ? string.Empty : $"{(isNewLine ? "<br/>" : "")}{@this}【{oldValue}】改为【{newValue}】";

        public static string ToLog(this string @this, DateTime oldValue, DateTime newValue, bool isNewLine = false)
        => oldValue == newValue ? string.Empty : $"{(isNewLine ? "<br/>" : "")}{@this}【{oldValue}】改为【{newValue}】";

        public static string ToLog(this string @this, long? oldValue, long? newValue, bool isNewLine = false)
        => @this.ToLog(oldValue.HasValue ? oldValue.Value : 0, newValue.HasValue ? newValue.Value : 0, isNewLine);

        public static string ToLog(this string @this, int? oldValue, int? newValue, bool isNewLine = false)
        => @this.ToLog(oldValue.HasValue ? oldValue.Value : 0, newValue.HasValue ? newValue.Value : 0, isNewLine);

        public static string ToLog(this string @this, decimal? oldValue, decimal? newValue, bool isNewLine = false)
        => @this.ToLog(oldValue.HasValue ? oldValue.Value : 0, newValue.HasValue ? newValue.Value : 0, isNewLine);

        public static string ToLog(this string @this, DateTime? oldValue, DateTime? newValue, bool isNewLine = false)
        => @this.ToLog(oldValue.HasValue ? oldValue.Value.ToString() : string.Empty, newValue.HasValue ? newValue.Value.ToString() : string.Empty, isNewLine);

        /// <summary>字符串混淆（同个字符串每次混淆后的值一样，但混淆后的值无法还原回原来的值）</summary>
        public static string Obfuscated(this string @this)
        {
            if (string.IsNullOrWhiteSpace(@this)) return "";
            var str = new StringBuilder();
            var length = @this.Length;
            for (int i = 0; i < length; i += 2)
            {
                var charNumber = (int)@this[i];
                if ((i + 1) < length)
                {
                    charNumber += (int)@this[i + 1];
                    str.Append(charNumber.ToString("X2").Reverse());
                }
            }
            return str.ToString();
        }

        /// <summary>判断指定的字符串是不是URL格式(http或https开头)</summary>
        public static bool IsUrl(this string @this)
        {
            if (!string.IsNullOrWhiteSpace(@this) &&
                (@this.StartsWith("http://", StringComparison.OrdinalIgnoreCase) || @this.StartsWith("https://", StringComparison.OrdinalIgnoreCase)))
            {
                return true;
            }
            return false;
        }

        public static byte[] ToByte(this string @this) => ToByte(@this, Encoding.UTF8);

        public static byte[] ToByte(this string @this, Encoding encoding) => encoding.GetBytes(@this);

        /// <summary>  
        /// 查询关键字方法  
        /// </summary>  
        /// <param name="this">原字符串</param>  
        /// <param name="keywords">关键字列表用|分开</param>  
        /// <returns>如果存在关键字返回true，反之返回false。</returns>  
        public static bool SearchKeywords(this string @this, string[] keywords)
        {
            if (String.IsNullOrWhiteSpace(@this))
                return false;
            if (keywords == null)
                return false;
            if (keywords.Length == 0)
                return false;

            int wordCount = keywords.Length;
            int[][] nexts = new int[wordCount][];
            int i = 0;
            for (i = 0; i < wordCount; i++)
            {
                nexts[i] = GetKmpNext(keywords[i]);
            }
            i = 0;
            int[] j = new int[nexts.Length];
            while (i < @this.Length)
            {
                for (int k = 0; k < wordCount; k++)
                {
                    if (@this[i] == keywords[k][j[k]])
                    {
                        j[k]++;
                    }
                    else
                    {
                        j[k] = nexts[k][j[k]];
                        if (j[k] == -1)
                        {
                            j[k]++;
                        }
                    }
                    if (j[k] >= keywords[k].Length)
                    {
                        return true;
                    }
                }
                i++;
            }
            return false;
        }

        /// <summary>
        /// /获取next  
        /// </summary>
        /// <param name="pattern"></param>
        /// <returns></returns>
        private static int[] GetKmpNext(string pattern)
        {

            int[] next = new int[pattern.Length];
            next[0] = -1;
            if (pattern.Length < 2)
                return next;
            next[1] = 0;
            int i = 2, j = 0;
            while (i < pattern.Length)
            {
                if (pattern[i - 1] == pattern[j])
                {
                    next[i++] = ++j;
                }
                else
                {
                    j = next[j];
                    if (j == -1)
                    {
                        next[i++] = ++j;
                    }
                }
            }
            return next;
        }

        public static bool IsNullOrWhiteSpace(this string @this) => string.IsNullOrWhiteSpace(@this);

        public static bool IsNotNullAndWhiteSpace(this string @this) => !string.IsNullOrWhiteSpace(@this);

        public static string GetUrlParamsValue(this string @this, string paramName)
        {
            if (@this.IsNullOrWhiteSpace()) return string.Empty;

            var reg = new Regex(@"(?:^|\?|&)" + paramName + "=(?<VAL>.+?)(?:&|$)", RegexOptions.IgnoreCase);
            Match m = reg.Match(@this);
            return m.Groups["VAL"].ToString();
        }

        public static string ToFilePath(this string @this) => Path.Combine(@this.SplitWithDirectorySeparatorChar());

        public static string[] SplitWithDirectorySeparatorChar(this string @this)
        => @this.Split(new char[] { Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar }, StringSplitOptions.RemoveEmptyEntries);

        public static string CombinePath(this string @this, string path)
        => $"{@this.TrimEnd(Path.DirectorySeparatorChar)}{Path.DirectorySeparatorChar}{path.ToFilePath()}";

        /// <summary>
        /// 获取网络地址上的图片
        /// </summary>
        /// <param name="imgUrl"></param>
        /// <returns></returns>
        public static byte[] GetImageByUrl(this string imgUrl)
        {
            if (string.IsNullOrWhiteSpace(imgUrl)) return null;

            var pattern = "http(s)?://([\\w-]+\\.)+[\\w-]+(/[\\w-./?%&=]*)?";

            if (!new Regex(pattern).IsMatch(imgUrl)) return null;

            try
            {
                using (var httpClient = new HttpClient())
                {
                    return httpClient.GetByteArrayAsync(imgUrl).Result;
                }
            }
            catch (Exception)
            {

            }
            return null;
        }

    }
}
